package com.aliang.shopping.security;

import com.aliang.shopping.exception.ServiceException;
import com.aliang.shopping.security.auth.Payload;
import com.aliang.shopping.security.auth.UserHolder;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * 重复提交切面
 */
@Aspect //定义为切面类
@Component //交给spring容器管理
@Slf4j // 日志注解
@AllArgsConstructor
@Order(1)// 优先级，数字越小优先级越高
public class RepeatSubmitAspect {

    private final RedissonClient redissonClient;
    private static final String RESUBMIT_LOCK_PREFIX = "resubmit:";
    private static final String COLON = ":";
    private static final String LINE = "-";

    @Around("@annotation(annotation)")
    public Object doAround(ProceedingJoinPoint joinPoint, PreventRepeatSubmit annotation) throws Throwable {
        //获取当前登录用户的信息
        Payload payload = UserHolder.get();
        // 登录用户信息不能为空
        if (Objects.nonNull(payload)) {
            //获取重复提交锁的 key
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            String resubmitLockKey = RESUBMIT_LOCK_PREFIX +
                    payload.getRole() + COLON +
                    payload.getId() + COLON +
                    request.getMethod() + LINE + request.getRequestURI();
            int expire = annotation.expireTime(); // 防重提交锁过期时间
            RLock lock = redissonClient.getLock(resubmitLockKey);
            // 获取锁失败，直接返回 false
            boolean lockResult = false;
            try {
                lockResult = lock.tryLock(0, expire, annotation.timeUnit());
                log.info("获取锁成功，{}", expire);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                // 解锁前检查当前线程是否持有该锁
                if (lock != null && lock.isHeldByCurrentThread()) {
                    log.info("关闭锁");
                    lock.unlock();
                }
            }
            //没有获取到锁，重复提交了
            if (!lockResult) {
                throw new ServiceException(
                        String.format("请勿重复提交,请%s秒后再试", expire)
                );
            }
        }
        return joinPoint.proceed();
    }


}
